import Koa from "koa"
import Router from '@koa/router'
import http from 'http'
import { WatchDog, searchDict, startBrowser, startYoudaoPage } from './lib.js'

/**
 * @import {Browser, Page} from 'puppeteer-core'
 */

const router = new Router()

async function start() {

    const app = new Koa()
    app.use(async (ctx, next) => {
        const ip = ctx.req.socket.remoteAddress

        console.info(ip, ctx.method, ctx.originalUrl)

        try {
            await next()
        } catch (err) {
            console.error(err)
            ctx.response.status = err.statusCode || err.status || 500
            ctx.response.body = err.stack
        }
    })

    app.use(router.routes())
    app.use(router.allowedMethods())

    let port = process.env.PORT || 54362

    http.createServer(app.callback()).listen(port)
    http.createServer(app.callback()).listen(32107)

    console.info(`service start at ${port} 32107`)

}

/**
 * @param {Koa.ParameterizedContext} ctx 
 * @returns {Promise<String>}
 */
async function getRequestBody(ctx) {
    return (await getRequestBodyBuffer(ctx)).toString('utf-8')
}
/**
 * @param {Koa.ParameterizedContext} ctx 
 * @returns {Promise<Buffer>}
 */
async function getRequestBodyBuffer(ctx) {
    return new Promise((resolve, reject) => {
        let buffer = []
        ctx.req.on('data', (chunk) => buffer.push(chunk))
        ctx.req.on('end', async () => {
            resolve(Buffer.concat(buffer))
        })
        ctx.req.on('error', reject)
        if (ctx.req.closed || ctx.req.errored) {
            reject(ctx.req.errored)
        }
    })
}

router.get('/status', async (ctx) => {
    ctx.body = {
        code: "success",
        msg: {},
    }
})

router.get('/front/health-status', async (ctx) => {
    ctx.body = {
        msg: "success",
        code: 0,
        data: {
        },
    }
})

/** @type{Promise<Browser>} */
let browser = null
/** @type{Promise<Page>} */
let page = null

let watchDog = new WatchDog(60_000, async () => {
    if (browser) {
        try {
            (await browser).close()
        } catch (error) {
            console.error(error)
        }
        browser = null
        page = null
    }
})

const map = new Map()

router.post('/api/search-dict', async (ctx) => {
    watchDog.feed()
    let body = await getRequestBody(ctx)
    let { word } = JSON.parse(body)
    if (map.has(word)) {
        ctx.body = { "result": { "code": 0, "msg": "success" }, "data": map.get(word) }
        return
    }
    if (!browser) {
        browser = startBrowser(true)
    }
    if (!page) {
        page = startYoudaoPage(await browser)
    }
    let data = await searchDict(await page, word)
    let entries = []
    if (data.result.code == 404) {
        entries = [{ entry: word, explain: word }]
        map.set(word, entries)
    }
    if (data.result.code == 200) {
        entries = data.data.entries
        map.set(word, entries)
    }
    if (entries.length == 0) {
        console.info(data)
        throw new Error(JSON.stringify(data))
    }
    ctx.body = { "result": { "code": 0, "msg": "success" }, "data": entries }
})

start()